home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / cvs-1_3.lha / cvs-1.3 / src / logmsg.c < prev    next >
C/C++ Source or Header  |  1992-04-09  |  12KB  |  450 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  */
  8.  
  9. #include "cvs.h"
  10.  
  11. #ifndef lint
  12. static char rcsid[] = "@(#)logmsg.c 1.40 92/04/10";
  13. #endif
  14.  
  15. #if __STDC__
  16. static int find_type (Node * p);
  17. static int fmt_proc (Node * p);
  18. static int logfile_write (char *repository, char *filter, char *title,
  19.               char *message, char *revision, FILE * logfp,
  20.               List * changes);
  21. static int rcsinfo_proc (char *repository, char *template);
  22. static int title_proc (Node * p);
  23. static int update_logfile_proc (char *repository, char *filter);
  24. static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes);
  25. static int editinfo_proc (char *repository, char *template);
  26. #else
  27. static void setup_tmpfile ();
  28. static int find_type ();
  29. static int fmt_proc ();
  30. static int rcsinfo_proc ();
  31. static int update_logfile_proc ();
  32. static int title_proc ();
  33. static int logfile_write ();
  34. static int editinfo_proc ();
  35. #endif                /* __STDC__ */
  36.  
  37. static FILE *fp;
  38. static char *strlist;
  39. static char *editinfo_editor;
  40. static Ctype type;
  41.  
  42. /*
  43.  * Puts a standard header on the output which is either being prepared for an
  44.  * editor session, or being sent to a logfile program.  The modified, added,
  45.  * and removed files are included (if any) and formatted to look pretty.
  46.  */
  47. static char *prefix;
  48. static int col;
  49. static void
  50. setup_tmpfile (xfp, xprefix, changes)
  51.     FILE *xfp;
  52.     char *xprefix;
  53.     List *changes;
  54. {
  55.     /* set up statics */
  56.     fp = xfp;
  57.     prefix = xprefix;
  58.  
  59.     type = T_MODIFIED;
  60.     if (walklist (changes, find_type) != 0)
  61.     {
  62.     (void) fprintf (fp, "%sModified Files:\n", prefix);
  63.     (void) fprintf (fp, "%s\t", prefix);
  64.     col = 8;
  65.     (void) walklist (changes, fmt_proc);
  66.     (void) fprintf (fp, "\n");
  67.     }
  68.     type = T_ADDED;
  69.     if (walklist (changes, find_type) != 0)
  70.     {
  71.     (void) fprintf (fp, "%sAdded Files:\n", prefix);
  72.     (void) fprintf (fp, "%s\t", prefix);
  73.     col = 8;
  74.     (void) walklist (changes, fmt_proc);
  75.     (void) fprintf (fp, "\n");
  76.     }
  77.     type = T_REMOVED;
  78.     if (walklist (changes, find_type) != 0)
  79.     {
  80.     (void) fprintf (fp, "%sRemoved Files:\n", prefix);
  81.     (void) fprintf (fp, "%s\t", prefix);
  82.     col = 8;
  83.     (void) walklist (changes, fmt_proc);
  84.     (void) fprintf (fp, "\n");
  85.     }
  86. }
  87.  
  88. /*
  89.  * Looks for nodes of a specified type and returns 1 if found
  90.  */
  91. static int
  92. find_type (p)
  93.     Node *p;
  94. {
  95.     if (p->data == (char *) type)
  96.     return (1);
  97.     else
  98.     return (0);
  99. }
  100.  
  101. /*
  102.  * Breaks the files list into reasonable sized lines to avoid line wrap...
  103.  * all in the name of pretty output.  It only works on nodes whose types
  104.  * match the one we're looking for
  105.  */
  106. static int
  107. fmt_proc (p)
  108.     Node *p;
  109. {
  110.     if (p->data == (char *) type)
  111.     {
  112.     if ((col + (int) strlen (p->key)) > 70)
  113.     {
  114.         (void) fprintf (fp, "\n%s\t", prefix);
  115.         col = 8;
  116.     }
  117.     (void) fprintf (fp, "%s ", p->key);
  118.     col += strlen (p->key) + 1;
  119.     }
  120.     return (0);
  121. }
  122.  
  123. /*
  124.  * Builds a temporary file using setup_tmpfile() and invokes the user's
  125.  * editor on the file.  The header garbage in the resultant file is then
  126.  * stripped and the log message is stored in the "message" argument.
  127.  * 
  128.  * rcsinfo - is the name of a file containing lines tacked onto the end of the
  129.  * RCS info offered to the user for editing. If specified, the '-m' flag to
  130.  * "commit" is disabled -- users are forced to run the editor.
  131.  * 
  132.  */
  133. void
  134. do_editor (dir, message, repository, changes)
  135.     char *dir;
  136.     char *message;
  137.     char *repository;
  138.     List *changes;
  139. {
  140.     static int reuse_log_message = 0;
  141.     char line[MAXLINELEN], fname[L_tmpnam+1];
  142.     char *orig_message;
  143.     struct stat pre_stbuf, post_stbuf;
  144.     int retcode = 0;
  145.  
  146.     if (noexec || reuse_log_message)
  147.     return;
  148.  
  149.     orig_message = xstrdup (message);    /* save it for later */
  150.  
  151.     /* Create a temporary file */
  152.     (void) tmpnam (fname);
  153.   again:
  154.     if ((fp = fopen (fname, "w+")) == NULL)
  155.     error (1, 0, "cannot create temporary file %s", fname);
  156.  
  157.     /* set up the file so that the first line is blank if no msg specified */
  158.     if (*orig_message)
  159.     {
  160.     (void) fprintf (fp, "%s", orig_message);
  161.     if (orig_message[strlen (orig_message) - 1] != '\n')
  162.         (void) fprintf (fp, "\n");
  163.     }
  164.     else
  165.     (void) fprintf (fp, "\n");
  166.  
  167.     /* tack templates on if necessary */
  168.     (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
  169.  
  170.     (void) fprintf (fp,
  171.   "%s----------------------------------------------------------------------\n",
  172.             CVSEDITPREFIX);
  173.     (void) fprintf (fp,
  174.   "%sEnter Log.  Lines beginning with `%s' are removed automatically\n%s\n",
  175.             CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX);
  176.     if (dir != NULL)
  177.     (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
  178.             dir, CVSEDITPREFIX);
  179.     setup_tmpfile (fp, CVSEDITPREFIX, changes);
  180.     (void) fprintf (fp,
  181.   "%s----------------------------------------------------------------------\n",
  182.             CVSEDITPREFIX);
  183.  
  184.     /* finish off the temp file */
  185.     (void) fclose (fp);
  186.     if (stat (fname, &pre_stbuf) == -1)
  187.     pre_stbuf.st_mtime = 0;
  188.  
  189.     if (editinfo_editor)
  190.     free (editinfo_editor);
  191.     editinfo_editor = (char *) NULL;
  192.     (void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
  193.  
  194.     /* run the editor */
  195.     run_setup ("%s", editinfo_editor ? editinfo_editor : Editor);
  196.     run_arg (fname);
  197.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
  198.                  RUN_NORMAL | RUN_SIGIGNORE)) != 0)
  199.     error (editinfo_editor ? 1 : 0, retcode == -1 ? errno : 0,
  200.            editinfo_editor ? "Logfile verification failed" :
  201.            "warning: editor session failed");
  202.  
  203.     /* put the entire message back into the message variable */
  204.     fp = open_file (fname, "r");
  205.     *message = '\0';
  206.     while (fgets (line, sizeof (line), fp) != NULL)
  207.     {
  208.     if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0)
  209.         continue;
  210.     if (((int) strlen (message) + (int) strlen (line)) >= MAXMESGLEN)
  211.     {
  212.         error (0, 0, "warning: log message truncated!");
  213.         break;
  214.     }
  215.     (void) strcat (message, line);
  216.     }
  217.     (void) fclose (fp);
  218.     if ((stat (fname, &post_stbuf) == 0 &&
  219.      pre_stbuf.st_mtime == post_stbuf.st_mtime) ||
  220.     (*message == '\0' || strcmp (message, "\n") == 0))
  221.     {
  222.     for (;;)
  223.     {
  224.         (void) printf ("\nLog message unchanged or not specified\n");
  225.         (void) printf ("a)bort, c)continue, e)dit, !)reuse this message unchanged for remaining dirs\n");
  226.         (void) printf ("Action: (continue) ");
  227.         (void) fflush (stdout);
  228.         *line = '\0';
  229.         (void) fgets (line, sizeof (line), stdin);
  230.         if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C')
  231.         break;
  232.         if (*line == 'a' || *line == 'A')
  233.         error (1, 0, "aborted by user");
  234.         if (*line == 'e' || *line == 'E')
  235.         goto again;
  236.         if (*line == '!')
  237.         {
  238.         reuse_log_message = 1;
  239.         break;
  240.         }
  241.         (void) printf ("Unknown input\n");
  242.     }
  243.     }
  244.     free (orig_message);
  245.     (void) unlink_file (fname);
  246. }
  247.  
  248. /*
  249.  * callback proc for Parse_Info for rcsinfo templates this routine basically
  250.  * copies the matching template onto the end of the tempfile we are setting
  251.  * up
  252.  */
  253. /* ARGSUSED */
  254. static int
  255. rcsinfo_proc (repository, template)
  256.     char *repository;
  257.     char *template;
  258. {
  259.     static char *last_template;
  260.     FILE *tfp;
  261.     char line[MAXLINELEN];
  262.  
  263.     /* nothing to do if the last one included is the same as this one */
  264.     if (last_template && strcmp (last_template, template) == 0)
  265.     return (0);
  266.     if (last_template)
  267.     free (last_template);
  268.     last_template = xstrdup (template);
  269.  
  270.     if ((tfp = fopen (template, "r")) != NULL)
  271.     {
  272.     while (fgets (line, sizeof (line), tfp) != NULL)
  273.         (void) fputs (line, fp);
  274.     (void) fclose (tfp);
  275.     return (0);
  276.     }
  277.     else
  278.     {
  279.     error (0, 0, "Couldn't open rcsinfo template file %s", template);
  280.     return (1);
  281.     }
  282. }
  283.  
  284. /*
  285.  * Uses setup_tmpfile() to pass the updated message on directly to any
  286.  * logfile programs that have a regular expression match for the checked in
  287.  * directory in the source repository.  The log information is fed into the
  288.  * specified program as standard input.
  289.  */
  290. static char *title;
  291. static FILE *logfp;
  292. static char *message;
  293. static char *revision;
  294. static List *changes;
  295.  
  296. void
  297. Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges)
  298.     char *repository;
  299.     char *xmessage;
  300.     char *xrevision;
  301.     FILE *xlogfp;
  302.     List *xchanges;
  303. {
  304.     char *srepos;
  305.  
  306.     /* set up static vars for update_logfile_proc */
  307.     message = xmessage;
  308.     revision = xrevision;
  309.     logfp = xlogfp;
  310.     changes = xchanges;
  311.  
  312.     /* figure out a good title string */
  313.     srepos = Short_Repository (repository);
  314.  
  315.     /* allocate a chunk of memory to hold the title string */
  316.     if (!strlist)
  317.     strlist = xmalloc (MAXLISTLEN);
  318.     strlist[0] = '\0';
  319.  
  320.     type = T_TITLE;
  321.     (void) walklist (changes, title_proc);
  322.     type = T_ADDED;
  323.     (void) walklist (changes, title_proc);
  324.     type = T_MODIFIED;
  325.     (void) walklist (changes, title_proc);
  326.     type = T_REMOVED;
  327.     (void) walklist (changes, title_proc);
  328.     title = xmalloc (strlen (srepos) + strlen (strlist) + 1 + 2); /* for 's */
  329.     (void) sprintf (title, "'%s%s'", srepos, strlist);
  330.  
  331.     /* to be nice, free up this chunk of memory */
  332.     free (strlist);
  333.     strlist = (char *) NULL;
  334.  
  335.     /* call Parse_Info to do the actual logfile updates */
  336.     (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1);
  337.  
  338.     /* clean up */
  339.     free (title);
  340. }
  341.  
  342. /*
  343.  * callback proc to actually do the logfile write from Update_Logfile
  344.  */
  345. static int
  346. update_logfile_proc (repository, filter)
  347.     char *repository;
  348.     char *filter;
  349. {
  350.     return (logfile_write (repository, filter, title, message, revision,
  351.                logfp, changes));
  352. }
  353.  
  354. /*
  355.  * concatenate each name onto strlist
  356.  */
  357. static int
  358. title_proc (p)
  359.     Node *p;
  360. {
  361.     if (p->data == (char *) type)
  362.     {
  363.     (void) strcat (strlist, " ");
  364.     (void) strcat (strlist, p->key);
  365.     }
  366.     return (0);
  367. }
  368.  
  369. /*
  370.  * Since some systems don't define this...
  371.  */
  372. #ifndef MAXHOSTNAMELEN
  373. #define    MAXHOSTNAMELEN    256
  374. #endif
  375.  
  376. /*
  377.  * Writes some stuff to the logfile "filter" and returns the status of the
  378.  * filter program.
  379.  */
  380. static int
  381. logfile_write (repository, filter, title, message, revision, logfp, changes)
  382.     char *repository;
  383.     char *filter;
  384.     char *title;
  385.     char *message;
  386.     char *revision;
  387.     FILE *logfp;
  388.     List *changes;
  389. {
  390.     char cwd[PATH_MAX], host[MAXHOSTNAMELEN];
  391.     FILE *pipefp, *Popen ();
  392.     char *prog = xmalloc (MAXPROGLEN);
  393.     char *cp;
  394.     int c;
  395.  
  396.     /*
  397.      * A maximum of 6 %s arguments are supported in the filter
  398.      */
  399.     (void) sprintf (prog, filter, title, title, title, title, title, title);
  400.     if ((pipefp = Popen (prog, "w")) == NULL)
  401.     {
  402.     if (!noexec)
  403.         error (0, 0, "cannot write entry to log filter: %s", prog);
  404.     free (prog);
  405.     return (1);
  406.     }
  407.     if (gethostname (host, sizeof (host)) < 0)
  408.     (void) strcpy (host, "(unknown)");
  409.     (void) fprintf (pipefp, "Update of %s\n", repository);
  410.     (void) fprintf (pipefp, "In directory %s:%s\n\n", host,
  411.             ((cp = getwd (cwd)) != NULL) ? cp : cwd);
  412.     if (revision && *revision)
  413.     (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision);
  414.     setup_tmpfile (pipefp, "", changes);
  415.     (void) fprintf (pipefp, "Log Message:\n%s\n", message);
  416.     if (logfp != (FILE *) 0)
  417.     {
  418.     (void) fprintf (pipefp, "Status:\n");
  419.     (void) rewind (logfp);
  420.     while ((c = getc (logfp)) != EOF)
  421.         (void) putc ((char) c, pipefp);
  422.     }
  423.     free (prog);
  424.     return (pclose (pipefp));
  425. }
  426.  
  427. /*
  428.  * We choose to use the *last* match within the editinfo file for this
  429.  * repository.  This allows us to have a global editinfo program for the
  430.  * root of some hierarchy, for example, and different ones within different
  431.  * sub-directories of the root (like a special checker for changes made to
  432.  * the "src" directory versus changes made to the "doc" or "test"
  433.  * directories.
  434.  */
  435. /* ARGSUSED */
  436. static int
  437. editinfo_proc(repository, editor)
  438.     char *repository;
  439.     char *editor;
  440. {
  441.     /* nothing to do if the last match is the same as this one */
  442.     if (editinfo_editor && strcmp (editinfo_editor, editor) == 0)
  443.     return (0);
  444.     if (editinfo_editor)
  445.     free (editinfo_editor);
  446.  
  447.     editinfo_editor = xstrdup (editor);
  448.     return (0);
  449. }
  450.